/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string>

#include "hilog_wrapper.h"
#include "napi/native_api.h"
#include "napi/native_node_api.h"
#include "securec.h"

static napi_value getHelloString(napi_env env, napi_callback_info info) {
  HILOG_INFO("hellonapi#%{public}s,called", __func__);
  napi_value result;
  std::string words = "Hello OpenHarmony NAPI";
  NAPI_CALL(env, napi_create_string_utf8(env, words.c_str(), words.length(), &result));
  return result;
}

// 测试一个有参数的js方法
static napi_value add(napi_env env, napi_callback_info info) {
  // 获取2个参数，值的类型是js类型（napi_value）
  size_t argc = 2;
  napi_value args[2];
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  // 获取并判断js参数类型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, NULL, "Wrong arguments. 2 numbers are expected.");
    return NULL;
  }

  // 将js类型（napi_value）的参数值转换成C++类型double，其他类型同理调用napi_get_value_typeX()
  double value0;
  NAPI_CALL(env, napi_get_value_double(env, args[0], &value0));

  double value1;
  NAPI_CALL(env, napi_get_value_double(env, args[1], &value1));

  // 业务逻辑运算
  double sum = value0 + value1;

  // 将结果由C++类型（double）转换成js类型（napi_value），其他类型同理调用napi_create_typeX()
  napi_value reuslt;
  NAPI_CALL(env, napi_create_double(env, sum, &reuslt));

  // 返回napi_value类型结果
  return reuslt;
}

struct AddonData {
  napi_async_work asyncWork = nullptr;
  napi_deferred deferred = nullptr;
  napi_ref callback = nullptr;
  double args[2] = {0};
  double result = 0;
};

// 业务逻辑处理函数，由worker线程池调度执行。
static void addExecuteCB(napi_env env, void *data) {
  AddonData *addonData = (AddonData *)data;

  // 执行复杂计算，不阻塞主线程。此处用一个加法简单示意。
  addonData->result = addonData->args[0] + addonData->args[1];
}

// 业务逻辑处理完成回调函数，在业务逻辑处理函数执行完成或取消后触发，由EventLoop线程中执行。
static void addCallbackCompleteCB(napi_env env, napi_status status, void *data) {
  AddonData *addonData = (AddonData *)data;
  napi_value callback = nullptr;
  napi_get_reference_value(env, addonData->callback, &callback);
  napi_value undefined = nullptr;
  napi_get_undefined(env, &undefined);
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_value callbackResult = nullptr;

  // 执行回调函数
  napi_call_function(env, undefined, callback, 1, &result, &callbackResult);

  // 删除napi_ref对象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 删除异步工作项
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
}

static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) {
  AddonData *addonData = (AddonData *)data;
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_resolve_deferred(env, addonData->deferred, result);

  // 删除napi_ref对象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 删除异步工作项
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
}

static napi_value addCallback(napi_env env, napi_callback_info info) {
  // 获取3个参数，值的类型是js类型（napi_value）
  size_t argc = 3;
  napi_value args[3];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 获取并判断js参数类型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
    return NULL;
  }

  napi_valuetype valuetype2;
  NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
  if (valuetype2 != napi_function) {
    napi_throw_type_error(env, nullptr, "Callback function expected.");
    return NULL;
  }

  // 异步工作项上下文用户数据，传递到异步工作项的execute、complete中传递数据
  auto addonData = new AddonData{
      .asyncWork = nullptr,
  };

  // 将接收到的参数传入用户自定义上下文数据
  NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
  NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));
  NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));

  // 创建async work，创建成功后通过最后一个参数接收async work的handle
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "addCallback", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addCallbackCompleteCB, (void *)addonData,
                         &addonData->asyncWork);

  // 将刚创建的async work加到队列，由底层去调度执行
  napi_queue_async_work(env, addonData->asyncWork);

  // 原生方法返回空对象
  napi_value result = 0;
  NAPI_CALL(env, napi_get_null(env, &result));
  return result;
}

static napi_value addPromise(napi_env env, napi_callback_info info) {
  // 获取2个参数，值的类型是js类型（napi_value）
  size_t argc = 2;
  napi_value args[2];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 获取并判断js参数类型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
    return NULL;
  }

  // 创建promise
  napi_value promise = nullptr;
  napi_deferred deferred = nullptr;
  NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));

  // 异步工作项上下文用户数据，传递到异步工作项的execute、complete之间传递数据
  auto addonData = new AddonData{
      .asyncWork = nullptr,
      .deferred = deferred,
  };

  // 将接收到的参数传入
  NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
  NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));

  // 创建async work，创建成功后通过最后一个参数(addonData->asyncWork)返回async work的handle
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "addPromise", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void *)addonData,
                         &addonData->asyncWork);

  // 将刚创建的async work加到队列，由底层去调度执行
  napi_queue_async_work(env, addonData->asyncWork);

  // 返回promise
  return promise;
}

static napi_value addAsync(napi_env env, napi_callback_info info) {
  // 获取3个参数，值的类型是js类型（napi_value）
  size_t argc = 3;
  napi_value args[3];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 获取并判断js参数类型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
    return NULL;
  }

  // 异步工作项上下文用户数据，传递到异步工作项的execute、complete中传递数据
  auto addonData = new AddonData{
      .asyncWork = nullptr,
  };

  if (argc == 2) {
    // 创建promise
    napi_value promise = nullptr;
    napi_deferred deferred = nullptr;
    NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
    addonData->deferred = deferred;

    // 将接收到的参数传入
    NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
    NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));

    // 创建async work，创建成功后通过最后一个参数(addonData->asyncWork)返回async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "addPromise", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void *)addonData,
                           &addonData->asyncWork);

    // 将刚创建的async work加到队列，由底层去调度执行
    napi_queue_async_work(env, addonData->asyncWork);

    // 返回promise
    return promise;
  } else {
    napi_valuetype valuetype2;
    NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
    if (valuetype2 != napi_function) {
      napi_throw_type_error(env, nullptr, "Callback function expected.");
      return NULL;
    }

    // 将接收到的参数传入用户自定义上下文数据
    NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
    NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));
    NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));

    // 创建async work，创建成功后通过最后一个参数接收async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "addCallback", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addCallbackCompleteCB, (void *)addonData,
                           &addonData->asyncWork);

    // 将刚创建的async work加到队列，由底层去调度执行
    napi_queue_async_work(env, addonData->asyncWork);

    // 原生方法返回空对象
    napi_value result = 0;
    NAPI_CALL(env, napi_get_null(env, &result));
    return result;
  }
}

// napi_addon_register_func
static napi_value registerFunc(napi_env env, napi_value exports) {
  static napi_property_descriptor desc[] = {
      DECLARE_NAPI_FUNCTION("getHelloString", getHelloString),
      DECLARE_NAPI_FUNCTION("add", add),
      DECLARE_NAPI_FUNCTION("addCallback", addCallback),
      DECLARE_NAPI_FUNCTION("addPromise", addPromise),
      DECLARE_NAPI_FUNCTION("addAsync", addAsync),
  };
  NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
  return exports;
}

/* 定义napi module
 * nm_modname: 模块名称，对应eTS使用为import nm_modname from '@ohos.ohos_shared_library_name'
 *             示例对应为：import hellonapi from '@ohos.hellonapi'
 * nm_register_func：API注册函数
 */
static napi_module helloModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerFunc,
    .nm_modname = "hellonapi",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

// ability module register
extern "C" __attribute__((constructor)) void helloModuleRegister() { napi_module_register(&helloModule); }